// import { nextTick } from "process";
import Dep, { popTarget, pushTarget } from "./dep";

let id = 0;

class Watcher {
  constructor(vm, exprOrFb, options,cb) {
    this.id = id++;
    this.renderWatcher = options;
    this.vm = vm;
    if (typeof exprOrFb ==='string') {
      this.getter = function (){
        return vm[exprOrFb]
      }
    }else{
      this.getter = exprOrFb;
    }
    this.cb = cb
    this.user = options.user
    this.deps = [];
    this.depsId = new Set();
    this.lazy = options.lazy;
    this.dirty = this.lazy; // 缓存值
    this.value = this.lazy ? undefined : this.get();
  }
  addDep(dep) {
    let id = dep.id;
    if (!this.depsId.has(id)) {
      this.deps.push(dep);
      this.depsId.add(id);
      dep.addSub(this); //watcher已经记住dep且完成去重了，此时让dep也记住watcer
    }
  }
  evaluate(){
    this.value = this.get() //获取到用户函数的返回值，
    this.dirty = false //标识为修改过的（脏）
  } 
  get() {
    pushTarget(this);
    let value = this.getter.call(this.vm);
    popTarget();
    return value
  }
  depend(){
   let i = this.deps.length
   while (i--){
    this.deps[i].depend() 
   }
  }
  update() {
    if (this.lazy) {
      // 如果 是计算属性，依赖的值发生变化了 就标识计算属性为脏值 重新计算
      this.dirty = true
    }else{
      queueWatcher(this); //异步
    // this.get(); //重新渲染
    }
  }
  run() {
    let oldValue = this.value 
    let newValue = this.get();
    if (this.user) {
      this.cb.call(this.vm,newValue,oldValue)
    }
  }
}
let queue = [];
let has = {};
let pending = false;
function flushSchedulerQueue() {
  let flushQueue = queue.slice(0);
  queue = [];
  has = {};
  pending = false;
  flushQueue.forEach((q) => q.run());
}
function queueWatcher(watcher) {
  const id = watcher.id;
  if (!has[id]) {
    queue.push(watcher);
    has[id] = true;
    // 不管update执行多少次，只执行一次更新渲染操作
    if (!pending) {
      nextTick(flushSchedulerQueue, 0);
      pending = true;
    }
  }
}

// nextTick没有直接使用某个api 而是采用优雅降级的方式
// 内部先采用promiese (ie不兼容) ——> MutationObserver(h5的api) ——>setImmediate setTimeout
// let timerFunc;
// if (Promise) {
//   timerFunc = () => {
//     Promise.resolve().then(flushCallbacks);
//   };
// }else if(MutationObserver){
//   let observer = new MutationObserver(flushCallbacks)
//   let textNode = document.createTextNode(1)
//   observer.observe(textNode,{
//     characterData:true
//   })
//   timerFunc = () =>{
//     textNode.textContent = 2
//   }
// }else if(setTimeout){
//   timerFunc = ()=>{
//     setTimeout(flushCallbacks)
//   }
// }

// nextTick的原理，当用户给属性进行赋值操作时，无法获取最新值，
// 这是因为由于事件循环机制，而vue的视图更新是异步任务，且相同数据改变只会触发更新一次。
// 我们获取值的操作是同步任务，如果想拿到更新后的值，就需要将取值操作变成异步任务，
// $nextTick要做的事就是将取值操作包装成异步任务并放到更新视图任务后面
let callbacks = [];
let waiting = false;
function flushCallbacks() {
  waiting = false;
  let cbs = callbacks.slice(0);
  callbacks = [];
  cbs.forEach((cb) => cb());
}
export function nextTick(cb) {
  callbacks.push(cb);
  if (!waiting) {
    Promise.resolve().then(flushCallbacks);
    waiting = true;
  }
}

// 需要给每个属性增加一个dep,目的就是收集watcher

export default Watcher;
